package com.macro.mall.portal.config.sharding.ranghash;

import cn.hutool.core.util.HashUtil;
import cn.hutool.core.util.ObjectUtil;
import com.macro.mall.common.utils.IdUtils;
import com.macro.mall.portal.domain.ShardFragmentTable;
import com.macro.mall.portal.domain.ShardGroup;
import com.macro.mall.portal.domain.ShardNode;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author wwj
 */
public class RangeHashAlgorithm {


    public static ShardGroup getShardGroup(String tableName, Long id) {
        List<ShardGroup> shardGroupList = RangeHashProperties.shardGroupList.stream().filter(s -> s.getName().equals(tableName))
                .collect(Collectors.toList());
        if (!shardGroupList.isEmpty()) {
            for (ShardGroup shardGroup : shardGroupList) {
                //isRange==true,查询的数据，扩容后的旧数据
                boolean isRange = shardGroup.getStartId() <= id && shardGroup.getEndId() >= id;
                if (isRange || shardGroup.isWritable()) {
                    return shardGroup;
                }
            }
        }
        throw new UnsupportedOperationException();
    }


    public static ShardNode getShardNode(String tableName, Long id) {
        List<ShardGroup> shardGroupList = RangeHashProperties.shardGroupList.stream().filter(s -> s.getName().equals(tableName))
                .collect(Collectors.toList());
        if (!shardGroupList.isEmpty()) {
            for (ShardGroup shardGroup : shardGroupList) {
                //isRange==true,查询的数据，扩容后的旧数据
                boolean isRange = shardGroup.getStartId() <= id && shardGroup.getEndId() >= id;
                if (isRange || shardGroup.isWritable()) {
                    return getShardNode(id, shardGroup);
                }
            }
        }
        throw new UnsupportedOperationException();
    }


    public static String getNodeName(String tableName, Long id) {
        List<ShardGroup> shardGroupList = RangeHashProperties.shardGroupList.stream().filter(s -> s.getName().equals(tableName))
                .collect(Collectors.toList());
        if (!shardGroupList.isEmpty()) {
            for (ShardGroup shardGroup : shardGroupList) {
                //isRange==true,查询的数据，扩容后的旧数据
                boolean isRange = shardGroup.getStartId() <= id && shardGroup.getEndId() >= id;
                if (isRange || shardGroup.isWritable()) {
                    return getNodeName(id, shardGroup);
                }
            }
        }
        throw new UnsupportedOperationException();
    }

    private static String getNodeName(Long id, ShardGroup shardGroup) {
        ShardNode shardNode = getShardNode(id, shardGroup);
        if (shardNode != null) {
            return shardNode.getName();
        }
        throw new UnsupportedOperationException();
    }

    private static ShardNode getShardNode(Long id, ShardGroup shardGroup) {
        long mod = HashUtil.pjwHash(IdUtils.generateDateTime(id) + "") % shardGroup.getNodeCount();
        List<ShardNode> shardNodes = RangeHashProperties.shardNodeMap.get(shardGroup.getId());
        Optional<ShardNode> first = shardNodes.stream().filter(node -> node.getHashValue() == mod).findFirst();
        ShardNode shardNode = first.orElse(null);
        return shardNode;
    }

    public static String getShardFragmentTableName(String table, long id) {
        ShardFragmentTable shardFragmentTable = getShardFragmentTable(table, id);
        if (ObjectUtil.isNotNull(shardFragmentTable)) {
            return shardFragmentTable.getName();
        }
        throw new UnsupportedOperationException();
    }


    public static ShardFragmentTable getShardFragmentTable(String table, long id) {
        ShardNode shardNode = RangeHashAlgorithm.getShardNode(table, id);
        List<ShardFragmentTable> shardFragmentTables = RangeHashProperties.shardFragmentMap.get(shardNode.getId());

        int tableMod = HashUtil.pjwHash(id + "") % shardNode.getFragmentCount();
        Optional<ShardFragmentTable> shardFragmentTableOptional = shardFragmentTables.stream().filter(s -> s.getHashValue() == tableMod).findFirst();
        return shardFragmentTableOptional.orElse(null);
    }


}
